"""
JAR包对比服务
"""
import os
import sys
import zipfile
import hashlib
from pathlib import Path
from typing import Dict, List, Optional, Set, Tuple
from collections import defaultdict

# 添加项目根目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from services.progress_service import progress_service
from utils_class_match.file_utils import normalize_path


class JarCompareService:
    """JAR包对比服务类"""
    
    def __init__(self, upload_folder: str):
        """
        初始化JAR对比服务
        
        Args:
            upload_folder: 上传文件目录
        """
        self.upload_folder = upload_folder
    
    def compare_jars(self, jar1_path: str, jar2_path: str, 
                    jar1_name: str = "JAR包1", jar2_name: str = "JAR包2",
                    progress_key: Optional[str] = None) -> Dict:
        """
        对比两个JAR包的所有文件，包括嵌套JAR包
        
        Args:
            jar1_path: 第一个JAR包路径
            jar2_path: 第二个JAR包路径
            jar1_name: 第一个JAR包名称
            jar2_name: 第二个JAR包名称
            progress_key: 进度键（任务ID）
        
        Returns:
            Dict: 对比结果，包含：
                - only_in_jar1: 只在JAR1中的文件
                - only_in_jar2: 只在JAR2中的文件
                - different_content: 内容不同的文件
                - same_content: 内容相同的文件
                - summary: 统计信息
        """
        if progress_key:
            progress_service.update_progress(progress_key, status='analyzing')
        
        # 收集两个JAR包中的所有文件信息
        jar1_files = self._collect_jar_files(jar1_path, jar1_name, progress_key, "jar1")
        if progress_key:
            progress_service.update_progress(progress_key, status='analyzing_jar2')
        
        jar2_files = self._collect_jar_files(jar2_path, jar2_name, progress_key, "jar2")
        
        if progress_key:
            progress_service.update_progress(progress_key, status='comparing')
        
        # 对比文件
        result = self._compare_file_sets(jar1_files, jar2_files, jar1_name, jar2_name, progress_key)
        
        return result
    
    def _collect_jar_files(self, jar_path: str, jar_name: str, 
                          progress_key: Optional[str], jar_id: str) -> Dict[str, Dict]:
        """
        收集JAR包中的所有文件信息（包括嵌套JAR包）
        
        Args:
            jar_path: JAR包路径
            jar_name: JAR包名称
            progress_key: 进度键
            jar_id: JAR包ID（用于进度跟踪）
        
        Returns:
            Dict: 文件信息字典，key为规范化路径，value为文件信息
        """
        files = {}
        
        try:
            with zipfile.ZipFile(jar_path, 'r') as jar:
                # 统计总文件数（用于进度计算）
                file_list = jar.namelist()
                total_files = len(file_list)
                
                if progress_key:
                    # 初始化或更新总进度
                    current_progress = progress_service.get_progress(progress_key)
                    if current_progress and current_progress.get('total', 0) > 0:
                        # 累加总文件数（通过直接更新内部数据）
                        with progress_service._lock:
                            if progress_key in progress_service._progress_data:
                                progress_service._progress_data[progress_key]['total'] += total_files
                    else:
                        progress_service.init_progress(progress_key, total_files)
                    progress_service.update_progress(progress_key, status='processing')
                
                # 遍历JAR包中的所有文件
                for entry_info in jar.infolist():
                    entry_path = normalize_path(entry_info.filename)
                    
                    # 更新进度
                    if progress_key:
                        progress_service.increment_processed(progress_key)
                    
                    # 跳过目录
                    if entry_path.endswith('/'):
                        continue
                    
                    # 检查是否是嵌套的JAR包（在lib目录下）
                    if entry_path.endswith('.jar') and 'lib' in entry_path.lower():
                        # 处理嵌套JAR包
                        nested_files = self._process_nested_jar(
                            jar, entry_info, entry_path, jar_name, progress_key
                        )
                        files.update(nested_files)
                    else:
                        # 处理普通文件
                        file_info = self._get_file_info(jar, entry_info, entry_path, jar_name)
                        # 使用规范化路径作为key（去除BOOT-INF等前缀）
                        normalized_path = self._normalize_file_path(entry_path, jar_name)
                        files[normalized_path] = file_info
        
        except Exception as e:
            print(f"Error collecting files from jar {jar_path}: {e}")
            if progress_key:
                progress_service.update_progress(progress_key, 
                                               status=f'error: {str(e)}')
        
        return files
    
    def _process_nested_jar(self, parent_jar: zipfile.ZipFile, 
                           entry_info: zipfile.ZipInfo, entry_path: str,
                           parent_jar_name: str, progress_key: Optional[str]) -> Dict[str, Dict]:
        """
        处理嵌套的JAR包
        
        Args:
            parent_jar: 父JAR包对象
            entry_info: 嵌套JAR的zip信息
            entry_path: 嵌套JAR的路径（如BOOT-INF/lib/xxx.jar）
            parent_jar_name: 父JAR包名称
            progress_key: 进度键
        
        Returns:
            Dict: 文件信息字典
        """
        files = {}
        temp_jar_path = os.path.join(self.upload_folder, f"temp_{Path(entry_path).name}")
        
        try:
            # 提取嵌套JAR包到临时文件
            with parent_jar.open(entry_info) as nested_jar_file:
                with open(temp_jar_path, 'wb') as temp_file:
                    temp_file.write(nested_jar_file.read())
            
            # 递归收集嵌套JAR包中的文件
            nested_jar_name = f"{parent_jar_name}/{Path(entry_path).name}"
            nested_files = self._collect_nested_jar_files(
                temp_jar_path, nested_jar_name, entry_path, progress_key
            )
            files.update(nested_files)
        
        except Exception as e:
            print(f"Error processing nested jar {entry_path}: {e}")
        
        finally:
            # 清理临时文件
            if os.path.exists(temp_jar_path):
                try:
                    os.remove(temp_jar_path)
                except:
                    pass
        
        return files
    
    def _collect_nested_jar_files(self, jar_path: str, jar_name: str, 
                                  nested_jar_path: str, 
                                  progress_key: Optional[str]) -> Dict[str, Dict]:
        """
        收集嵌套JAR包中的文件
        
        Args:
            jar_path: 嵌套JAR包路径
            jar_name: JAR包名称（包含父路径）
            nested_jar_path: 嵌套JAR在父JAR中的路径
            progress_key: 进度键
        
        Returns:
            Dict: 文件信息字典
        """
        files = {}
        
        try:
            with zipfile.ZipFile(jar_path, 'r') as jar:
                for entry_info in jar.infolist():
                    entry_path = normalize_path(entry_info.filename)
                    
                    # 更新进度
                    if progress_key:
                        progress_service.increment_processed(progress_key)
                    
                    # 跳过目录
                    if entry_path.endswith('/'):
                        continue
                    
                    # 处理文件
                    file_info = self._get_file_info(jar, entry_info, entry_path, jar_name)
                    # 构建完整路径：嵌套JAR路径 + 文件路径
                    full_path = f"{nested_jar_path}/{entry_path}"
                    normalized_path = self._normalize_file_path(full_path, jar_name)
                    files[normalized_path] = file_info
        
        except Exception as e:
            print(f"Error collecting nested jar files {jar_path}: {e}")
        
        return files
    
    def _get_file_info(self, jar: zipfile.ZipFile, entry_info: zipfile.ZipInfo, 
                      entry_path: str, jar_name: str) -> Dict:
        """
        获取文件信息（包括内容哈希值）
        
        Args:
            jar: JAR包对象
            entry_info: 文件zip信息
            entry_path: 文件路径
            jar_name: JAR包名称
        
        Returns:
            Dict: 文件信息，包含path, jar_name, size, hash等
        """
        try:
            # 读取文件内容
            with jar.open(entry_info) as file_obj:
                content = file_obj.read()
            
            # 计算文件哈希值（使用MD5，速度快且足够精确）
            file_hash = hashlib.md5(content).hexdigest()
            
            return {
                'path': entry_path,
                'jar_name': jar_name,
                'size': entry_info.file_size,
                'hash': file_hash,
                'full_path': entry_path  # 保留原始路径用于显示
            }
        except Exception as e:
            print(f"Error getting file info for {entry_path}: {e}")
            return {
                'path': entry_path,
                'jar_name': jar_name,
                'size': 0,
                'hash': '',
                'full_path': entry_path,
                'error': str(e)
            }
    
    def _normalize_file_path(self, entry_path: str, jar_name: str) -> str:
        """
        规范化文件路径（去除BOOT-INF等前缀）
        
        Args:
            entry_path: 原始路径
            jar_name: JAR包名称
        
        Returns:
            str: 规范化后的路径
        """
        path = entry_path.replace('\\', '/')
        
        # 对于嵌套JAR包中的文件，去除嵌套JAR路径前缀
        if '.jar/' in path or '.jar\\' in path:
            # 提取.jar/之后的部分
            if '.jar/' in path:
                path = path.split('.jar/', 1)[1]
            elif '.jar\\' in path:
                path = path.split('.jar\\', 1)[1]
        
        # 去除BOOT-INF/classes/前缀（不区分大小写）
        if path.lower().startswith('boot-inf/classes/'):
            path = path[len('boot-inf/classes/'):]
        
        # 去除BOOT-INF/lib/xxx.jar/前缀（不区分大小写）
        if path.lower().startswith('boot-inf/lib/'):
            # 提取lib/之后的部分，但需要去除jar文件名
            parts = path.split('/', 3)
            if len(parts) >= 4 and parts[2].endswith('.jar'):
                path = parts[3] if len(parts) > 3 else ''
        
        return path
    
    def _compare_file_sets(self, jar1_files: Dict[str, Dict], 
                          jar2_files: Dict[str, Dict],
                          jar1_name: str, jar2_name: str,
                          progress_key: Optional[str]) -> Dict:
        """
        对比两个文件集合
        
        Args:
            jar1_files: JAR1的文件字典
            jar2_files: JAR2的文件字典
            jar1_name: JAR1名称
            jar2_name: JAR2名称
            progress_key: 进度键
        
        Returns:
            Dict: 对比结果
        """
        all_paths = set(jar1_files.keys()) | set(jar2_files.keys())
        total_paths = len(all_paths)
        
        if progress_key:
            # 更新总数为对比的文件数（通过直接更新内部数据）
            with progress_service._lock:
                if progress_key in progress_service._progress_data:
                    progress_service._progress_data[progress_key]['total'] += total_paths
            progress_service.update_progress(progress_key, processed=0)
        
        only_in_jar1 = []
        only_in_jar2 = []
        different_content = []
        same_content = []
        
        processed = 0
        for path in all_paths:
            processed += 1
            if progress_key:
                progress_service.update_progress(progress_key, processed=processed)
            
            file1 = jar1_files.get(path)
            file2 = jar2_files.get(path)
            
            if file1 and not file2:
                # 只在JAR1中
                only_in_jar1.append({
                    'path': path,
                    'jar_name': file1['jar_name'],
                    'size': file1['size'],
                    'full_path': file1.get('full_path', path)
                })
            elif file2 and not file1:
                # 只在JAR2中
                only_in_jar2.append({
                    'path': path,
                    'jar_name': file2['jar_name'],
                    'size': file2['size'],
                    'full_path': file2.get('full_path', path)
                })
            elif file1 and file2:
                # 两个JAR包都有，比较内容
                if file1['hash'] == file2['hash']:
                    # 内容相同
                    same_content.append({
                        'path': path,
                        'jar1_name': file1['jar_name'],
                        'jar2_name': file2['jar_name'],
                        'size': file1['size'],
                        'hash': file1['hash'],
                        'full_path': file1.get('full_path', path)
                    })
                else:
                    # 内容不同
                    different_content.append({
                        'path': path,
                        'jar1_name': file1['jar_name'],
                        'jar2_name': file2['jar_name'],
                        'jar1_size': file1['size'],
                        'jar2_size': file2['size'],
                        'jar1_hash': file1['hash'],
                        'jar2_hash': file2['hash'],
                        'full_path': file1.get('full_path', path)
                    })
        
        # 生成统计信息
        summary = {
            'total_files_jar1': len(jar1_files),
            'total_files_jar2': len(jar2_files),
            'only_in_jar1_count': len(only_in_jar1),
            'only_in_jar2_count': len(only_in_jar2),
            'different_content_count': len(different_content),
            'same_content_count': len(same_content),
            'total_compared': len(all_paths)
        }
        
        return {
            'only_in_jar1': only_in_jar1,
            'only_in_jar2': only_in_jar2,
            'different_content': different_content,
            'same_content': same_content,
            'summary': summary,
            'jar1_name': jar1_name,
            'jar2_name': jar2_name
        }

